package com.htfyun.eink.pw.draw;

import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Looper;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.htfyun.eink.pw.bitmap.Util;
import com.htfyun.eink.pw.bitmap.bitmap_recycle.BitmapPool;
import com.htfyun.eink.pw.bitmap.bitmap_recycle.LruBitmapPool;
import com.htfyun.eink.pw.configs.ConfigTopView;
import com.htfyun.eink.pw.configs.PWConfig;
import com.htfyun.eink.pw.core.IImeStateListener;
import com.htfyun.eink.pw.core.ctrl.PWCoreHelper;
import com.htfyun.eink.pw.draw.cache.PWPathCacheManager;
import com.htfyun.eink.pw.utils.DebugLog;
import com.htfyun.eink.pw.utils.PWUtils;

import java.util.ArrayList;
import java.util.List;

public class PWDrawPropertyImpl implements IPWDrawProperty {

    protected final static int BITMAP_POOL_TARGET_SCREENS = 2;

    /**
     * 传给底层的 bmp
     */
    protected Bitmap bmpPW;

    protected final Canvas canvasPW = new Canvas();

    protected final BitmapPool bitmapPool;

    // 用于 pointer 在 top-view 和 ime 上的 clip out 绘制;
    protected final Path clipOutPath = new Path();
    @NonNull
    protected final List<Rect> clipOutRects = new ArrayList<>();

    // 输入法 ime, 是屏幕的绝对位置
    protected final Rect imeRect = new Rect();
    protected final RectF tempRectF = new RectF();

    private final PWPaintConfig pwPaintConfig = new PWPaintConfig();

    @NonNull
    private final PWPathCacheManager pathCacheManager;

    protected PWDrawPropertyImpl() {
        bitmapPool = new LruBitmapPool(getTargetBitmapPoolSize());
        pathCacheManager = new PWPathCacheManager(this);
    }

    @Nullable
    @Override
    public View getBindView() {
        return null;
    }

    @NonNull
    @Override
    public Bitmap getBitmapOfPW() {
        if (bmpPW == null || bmpPW.isRecycled()) {
            //dummy
            return Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
        }
        return bmpPW;
    }

    @NonNull
    @Override
    public Canvas getCanvasPW() {
        return canvasPW;
    }

    @NonNull
    @Override
    public List<Rect> getClipOutRects() {
        return clipOutRects;
    }

    @Override
    public void computeClipOutPathByRects(@NonNull Path clipOutPath, @NonNull List<Rect> rects) {
        clipOutPath.reset();
        for (Rect rect : rects) {
            tempRectF.set(rect);
            clipOutPath.addRect(tempRectF, Path.Direction.CCW);
        }
    }

    @NonNull
    @Override
    public PWPathCacheManager getPathCacheManager() {
        return pathCacheManager;
    }

    @Override
    public void invalidate() {
        View bindView = getBindView();
        if (bindView == null) {
            return;
        }
        if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
            bindView.invalidate();
        } else {
            bindView.postInvalidate();
        }
    }

    @Override
    public void invalidate(Rect dirty) {
        View bindView = getBindView();

        if (bindView == null || dirty.isEmpty()) {
            return;
        }

        if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
            bindView.invalidate(dirty);
        } else {
            bindView.postInvalidate(dirty.left, dirty.top, dirty.right, dirty.bottom);
        }
    }

    @NonNull
    @Override
    public PWPaintConfig getPaintConfig() {
        return pwPaintConfig;
    }

    @Override
    public void drawPath(@NonNull Path path, @NonNull Paint paint, @Nullable Rect outPathRect) {
        computeClipOutPathByRects(clipOutPath, getClipOutRects());

        Canvas canvas = getCanvasPW();
        canvas.save();
        canvas.clipOutPath(clipOutPath);
        canvas.drawPath(path, paint);
        canvas.restore();

        if (outPathRect != null) {
            path.computeBounds(tempRectF, true);
            tempRectF.inset(-paint.getStrokeWidth(), -paint.getStrokeWidth());
            outPathRect.set(PWUtils.toCeil(tempRectF.left),
                    PWUtils.toCeil(tempRectF.top),
                    PWUtils.toCeil(tempRectF.right),
                    PWUtils.toCeil(tempRectF.bottom));
        }
    }

    public void initBitmapAndCanvas(int width, int height) {
        if (width <= 0 || height <= 0) {
            return;
        }

        putBitmaps();
        bmpPW = bitmapPool.get(width, height, Bitmap.Config.ARGB_8888);

        canvasPW.setBitmap(bmpPW);

        registerImeStateListener();

        getPWConfig().configTopView.addOnTopViewChangedListener(onTopViewChangedListener);

        Log.i(TAG, "initBitmapAndCanvas success");
    }

    public void deinitBitmapAndCanvas() {

        canvasPW.setBitmap(null);

        putBitmaps();

        unregisterImeStateListener();
        getPWConfig().configTopView.removeOnTopViewChangedListener(onTopViewChangedListener);

        Log.i(TAG, "deinitBitmapAndCanvas success.");

    }

    private void putBitmaps() {
        if (bmpPW != null && !bmpPW.isRecycled()) {
            bitmapPool.put(bmpPW);
            bmpPW = null;
        }
    }

    private int getTargetBitmapPoolSize() {
        DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
        int widthPixels = metrics.widthPixels;
        int heightPixels = metrics.heightPixels;

        int screenSize = widthPixels * heightPixels * Util.getBytesPerPixel(Bitmap.Config.ARGB_8888);
        return Math.round(screenSize * BITMAP_POOL_TARGET_SCREENS);
    }

    private void updateClipOutRects() {
        clipOutRects.clear();
        clipOutRects.addAll(getPWConfig().configTopView.getTopViewValidRectList());
        clipOutRects.add(imeRect);
    }

    private void registerImeStateListener() {
        PWCoreHelper.registerImeStateListener(imeStateListener);
    }

    private void unregisterImeStateListener() {
        PWCoreHelper.unregisterImeStateListener(imeStateListener);
    }

    private final IImeStateListener imeStateListener = (imeVisible, imeHeight) -> {
        DebugLog.e("imeVisible = " + imeVisible + ", imeHeight = " + imeHeight);

        if (!imeVisible) {
            imeRect.setEmpty();
            return;
        }
        DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
        int screenWidth = metrics.widthPixels;
        int screenHeight = metrics.heightPixels;
        imeRect.set(0, screenHeight - imeHeight, screenWidth, screenHeight);
        updateClipOutRects();
    };

    private final ConfigTopView.OnTopViewChangedListener onTopViewChangedListener = configTopView -> updateClipOutRects();

    protected PWConfig getPWConfig() {
        return PWConfig.Instance;
    }
}
